package com.easydisk.user.util;


import com.easydisk.user.config.RedisServer;
import lombok.extern.log4j.Log4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.security.Key;

/**
 * redis滑动窗口用户请求限流
 */
@Component
public class SlidingWindowCounter {
    /**
     * 前缀
     */
    private static final String SLIDING_WINDOW = "sliding_window_";

    private static final Logger LOGGER = LoggerFactory.getLogger(SlidingWindowCounter.class);

    @Autowired
    private RedisServer redisServer;

    @Autowired
    private RedisTemplate redisTemplate;

    // 窗口大小, 单位：毫秒
    long windowTime = 1000;
    // 每 窗口大小时间 最多 多少个请求
    int maxCount = 5;
    long ttl = 10000;

    /**
     * 判断key的value中有效访问次数是否超过了最大限定值maxCount,可通过用户名或者ip来进行限流
     * @param key
     * @param windowInSecond   窗口间隔，秒
     * @param maxCount   最大限定值
     * @return
     */
    public boolean overMaxCount(String key,int windowInSecond,Long maxCount){
        key=SLIDING_WINDOW+key;
        LOGGER.info("redis key = {}", key);
        //当前时间
        long currentMs=System.currentTimeMillis();
        //窗口开始时间
        long windowStarMs=currentMs-windowInSecond*1000L;
        //按score统计key的value中的有效数量
        Long count=redisServer.zSetGetCount(key,windowStarMs,currentMs);
        //已经访问次数》=最大课访问值
        return count>=maxCount;
    }

    /**
     *  判断key的value中的有效访问次数是否超过最大限定值maxCount，若没超过，调用increment方法，将窗口内的访问数加一
     * @param key
     * @param windowInSecond
     * @param maxCount
     * @return
     */
    public boolean canAccess(String key,int windowInSecond,long maxCount){
        key=SLIDING_WINDOW+key;
        //按照key统计集合中的有效数量
        Long count=redisServer.zSetGetSize(key);
        //按key统计集合中的有效数量
        if(count<maxCount){
            //将访问数增加一
            redisTemplate.opsForValue().increment(key,windowInSecond);
            return true;
        }else
            return false;
    }


    /**
     * 滑动窗口计数增长
     * @param key
     * @param windowInSecond
     */
    public void increment(String key,Integer windowInSecond){
        //当前时间
        long currentMs=System.currentTimeMillis();
        //窗口开始时间
        long windowStarMs=currentMs-windowInSecond*1000;
        //清除窗口过期成员
        redisServer.zSetRemoveRangeByScore(key,0,windowStarMs);
        //添加当前时间value=当前时间戳，score=当前时间戳
        redisServer.setzSetAdd(key,String.valueOf(currentMs),currentMs);
        //设置key过期时间

    }

}
